package it.netsphere.nswing.comp;

import it.netsphere.nswing.event.NSEvent;
import it.netsphere.nswing.event.NSEventManager;
import it.netsphere.nswing.util.ComboItem;

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.JTextComponent;

/**
 * 
 * @author alberto.goldoni
 */
public abstract class NSPanel extends JPanel {

    /**
	 * 
	 */
    private static final long serialVersionUID = 4257647700209761888L;

    private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(NSPanel.class);

    // private org.jdesktop.application.ResourceMap resourceMap;
    // private org.jdesktop.application.ResourceMap defaultResourceMap;

    protected abstract Properties loadProperties(String name);

    private Map<Class<?>, Properties> propertiesMap = new HashMap<Class<?>, Properties>();

    protected Properties getProperties(Class<?> cl) {
        Properties prop = propertiesMap.get(cl);
        if (prop == null) {
            prop = loadProperties(cl.getSimpleName() + ".properties");
            if (prop == null)
                prop = new Properties();
            propertiesMap.put(cl, prop);
        }

        return prop;
    }

    private Map<String, JComponent> componentMap = new HashMap<String, JComponent>();

    public NSPanel() {
        it.netsphere.nswing.event.NSEventManager.getInstance().addUIListener(this);
    }

    public void freeComponent(JComponent comp) {
        componentMap.remove(comp.getName());
    }

    private boolean getResourceBoolean(JComponent comp, String suffix) {
        String s = getResource(comp, suffix);
        if (s == null)
            return false;

        return Boolean.parseBoolean(s);
    }

    private String getResource(JComponent comp, String suffix) {
        if (comp == null)
            return null;

        String propName = comp.getName() + "." + suffix;

        Properties prop = null;
        Class<?> cl = comp.getClass();

        while (JComponent.class.isAssignableFrom(cl)) {
            try {
                if (cl.equals(JComponent.class))
                    propName = "default." + suffix;

                prop = getProperties(cl);
                String value = prop.getProperty(propName);
                if (value != null)
                    return value;
            } catch (IllegalArgumentException e) {
                LOG.warn(e.getMessage(), e);
            }
            cl = cl.getSuperclass();
        }

        return null;
    }

    private String getResourceText(JComponent comp) {
        String s = getResource(comp, "text");
        return s;
    }

    /**
     * 
     * @param comp
     */

    protected void applyResources(JComponent comp) {
        if (comp.getName() == null)
            return;

        applyText(comp);
        applyColor(comp);
        applyFont(comp);
        applyBorder(comp);
        applyIcon(comp);
        applyColumns(comp);
        applyTable(comp);
        applyRegExp(comp);
        applyDomain(comp);
        applyEditable(comp);
        applyEnable(comp);
        applyTooltip(comp);
        applyGroup(comp);

    }

    private void applyFont(JComponent comp) {
        if (comp == null)
            return;

        String fontStyle = getResource(comp, "fontStyle");
        String fontName = getResource(comp, "fontName");
        String fontSizeStr = getResource(comp, "fontSize");

        Font f = comp.getFont();

        int fontSize = f.getSize();
        ;

        try {
            fontSize = Integer.parseInt(fontSizeStr);
        } catch (Exception e) {
        }

        int style = f.getStyle();

        if (fontStyle != null) {
            if (fontStyle.equalsIgnoreCase("BOLD"))
                style = Font.BOLD;
            else if (fontStyle.equalsIgnoreCase("ITALIC"))
                style = Font.ITALIC;
            else if (fontStyle.equalsIgnoreCase("PLAIN"))
                style = Font.PLAIN;

        }

        Font f2 = new Font(fontName != null ? fontName : f.getName(), style, fontSize);
        comp.setFont(f2);
    }

    /**
     * 
     * @param comp
     */

    private void applyColumns(JComponent comp) {
        if (comp == null)
            return;

        if (!(comp instanceof JTextComponent))
            return;

        String columnStr = getResource(comp, "columns");
        int columns = 10;
        try {
            columns = Integer.parseInt(columnStr);
        } catch (Exception e) {
        }

        if (comp instanceof JTextArea) {
            JTextArea ta = (JTextArea) comp;
            ta.setColumns(columns);
        } else if (comp instanceof JTextField) {
            JTextField ta = (JTextField) comp;
            ta.setColumns(columns);
        }

    }

    private void applyRegExp(JComponent comp) {
        if (comp == null)
            return;

        String regexp = getResource(comp, "regexp");

        if (comp instanceof NSTextFieldRegExp) {
            NSTextFieldRegExp ta = (NSTextFieldRegExp) comp;
            ta.setRegExp(regexp);
        }

        if (comp instanceof NSTextAreaRegExp) {
            NSTextAreaRegExp ta = (NSTextAreaRegExp) comp;
            ta.setRegExp(regexp);
        }

        if (comp instanceof NSComboBox) {
            NSComboBox combo = (NSComboBox) comp;
            combo.setRegExp(regexp);
        }
    }

    @SuppressWarnings("unchecked")
    private void applyDomain(JComponent comp) {
        if (comp == null)
            return;

        String domain = getResource(comp, "domain");

        if (comp instanceof NSComboBox) {
            NSComboBox combo = (NSComboBox) comp;

            try {
                ComboItem[] items = ComboItem.readFromString(domain);

                combo.removeAllItems();
                for (ComboItem item : items)
                    combo.addItem(item);
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        }

    }

    private void applyEnable(JComponent comp) {
        if (comp == null)
            return;

        String enableString = getResource(comp, "enable");
        if (enableString == null)
            return;

        boolean enable = true;
        try {
            enable = Boolean.parseBoolean(enableString);
        } catch (Exception e) {
        }

        comp.setEnabled(enable);
    }

    private void applyEditable(JComponent comp) {
        if (comp == null)
            return;

        String editableString = getResource(comp, "editable");
        if (editableString == null)
            return;

        boolean editable = true;
        try {
            editable = Boolean.parseBoolean(editableString);
        } catch (Exception e) {
        }

        if (comp instanceof JTextField) {
            JTextField comp2 = (JTextField) comp;
            comp2.setEditable(editable);
        }

    }

    private void applyTooltip(JComponent comp) {
        if (comp == null)
            return;

        String tooltip = getResource(comp, "tooltip");
        if (tooltip == null)
            return;

        comp.setToolTipText(tooltip);

    }

    private void applyGroup(JComponent comp) {
        if (comp == null)
            return;

        String group = getResource(comp, "group");
        if (group == null)
            return;

        if (JRadioButton.class.isAssignableFrom(comp.getClass())) {
            JRadioButton rb = (JRadioButton) comp;
            ButtonGroup bg = createButtonGroup(group);
            bg.add(rb);
            if (NSRadioButton.class.isAssignableFrom(comp.getClass())) {
                NSRadioButton rb2 = (NSRadioButton) comp;
                rb2.setButtonGroup(bg);
            }
        }

    }

    private void applyTable(JComponent comp) {
        if (comp == null)
            return;

        String columns = getResource(comp, "table.columns");
        if (columns != null) {
            if (comp instanceof NSTable) {
                NSTable comp2 = (NSTable) comp;
                comp2.setColumnProperties(columns);
            }
        }

        boolean autoresize = getResourceBoolean(comp, "table.autoresize");
        if (comp instanceof NSTable) {
            NSTable comp2 = (NSTable) comp;
            comp2.setAutoResize(autoresize);
        }

        boolean sorter = getResourceBoolean(comp, "table.sorter");
        if (comp instanceof NSTable) {
            NSTable comp2 = (NSTable) comp;
            comp2.setSorterEnable(sorter);
        }

        String fontStyle = getResource(comp, "table.header.fontStyle");
        String fontName = getResource(comp, "table.header.fontName");
        String fontSizeStr = getResource(comp, "table.header.fontSize");
        if (fontStyle != null || fontName != null || fontSizeStr != null) {

            Font f = comp.getFont();

            int fontSize = f.getSize();
            ;

            try {
                fontSize = Integer.parseInt(fontSizeStr);
            } catch (Exception e) {
            }

            int style = f.getStyle();

            if (fontStyle != null) {
                if (fontStyle.equalsIgnoreCase("BOLD"))
                    style = Font.BOLD;
                else if (fontStyle.equalsIgnoreCase("ITALIC"))
                    style = Font.ITALIC;
                else if (fontStyle.equalsIgnoreCase("PLAIN"))
                    style = Font.PLAIN;

            }

            Font f2 = new Font(fontName != null ? fontName : f.getName(), style, fontSize);
            if (comp instanceof NSTable) {
                NSTable comp2 = (NSTable) comp;
                comp2.getTableHeader().setFont(f2);

            }

        }
    }

    /**
     * 
     * @param comp
     */

    private void applyText(JComponent comp) {

        String text = getResourceText(comp);
        if (text == null)
            return;

        if (comp instanceof JRadioButton) {
            JRadioButton rb = (JRadioButton) comp;
            rb.setText(text);
        } else if (comp instanceof AbstractButton) {
            AbstractButton rb = (AbstractButton) comp;
            rb.setText(text);
        } else if (comp instanceof JLabel) {
            JLabel lab = (JLabel) comp;
            lab.setText(text);
        } else if (comp instanceof JTextComponent) {
            JTextComponent tf = (JTextComponent) comp;
            tf.setText(text == null || text.equals("<undefined>") ? "" : text);
        } else if (comp instanceof JMenuItem) {
            JMenuItem menuItem = (JMenuItem) comp;
            menuItem.setText(text);
        } else if (comp instanceof JMenu) {
            JMenu menu = (JMenu) comp;
            menu.setText(text);
        }
    }

    /**
     * 
     * @param comp
     */

    @SuppressWarnings("unchecked")
    private void applyBorder(JComponent comp) {
        String borderName = getResource(comp, "borderName");
        if (borderName == null)
            return;

        try {
            Class<? extends Border> cl = (Class<? extends Border>) Class.forName(borderName);
            Border b = cl.newInstance();
            comp.setBorder(b);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }

        if (comp instanceof JRadioButton) {
            JRadioButton rb = (JRadioButton) comp;
            rb.setBorderPainted(true);
        }
    }

    private void applyColor(JComponent comp) {
        if (comp == null)
            return;

        String colors = getResource(comp, "color");
        if (colors == null)
            return;
        Color color = null;
        if (colors.equalsIgnoreCase("red"))
            color = Color.RED;

        if (color == null)
            try {
                color = new Color(Integer.parseInt(colors, 16));
            } catch (Exception e) {
                LOG.debug(e.getMessage(), e);
            }

        if (color == null)
            color = Color.BLACK;

        if (JTextComponent.class.isAssignableFrom(comp.getClass())) {
            JTextComponent comp2 = (JTextComponent) comp;
            comp2.setForeground(color);
        } else if (JLabel.class.isAssignableFrom(comp.getClass())) {
            JLabel comp2 = (JLabel) comp;
            comp2.setForeground(color);
        }

    }

    private void applyIcon(JComponent comp) {
        String iconName = getResource(comp, "icon");
        if (iconName == null)
            return;

        try {
            Icon icon = null;
            URL url = getClass().getResource("resources/" + iconName);
            if (url == null)
                return;
            icon = new ImageIcon(url);

            if (comp instanceof JLabel) {
                JLabel l = (JLabel) comp;
                l.setIcon(icon);
            }

            if (comp instanceof JButton) {
                JButton b = (JButton) comp;
                b.setIcon(icon);
            }

        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }

    }

    protected final NSRadioButton createRadioButton(String name, final String eventCode) {
        NSRadioButton rb = (NSRadioButton) componentMap.get(name);
        if (rb != null)
            return rb;

        rb = new NSRadioButton();
        rb.setName(name);
        applyResources(rb);

        if (eventCode != null)
            rb.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    NSEventManager.fireUIEvent(NSPanel.this, eventCode);
                }
            });

        componentMap.put(name, rb);
        return rb;
    }

    /**
     * 
     * @param name
     * @param eventCode
     * @return
     */

    protected final JButton createButton(String name, final String eventCode) {
        JButton b = (JButton) componentMap.get(name);
        if (b != null)
            return b;

        b = new JButton();
        b.setName(name);
        applyResources(b);

        if (eventCode != null)
            b.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    NSEventManager.fireUIEvent(NSPanel.this, eventCode);
                }
            });

        componentMap.put(name, b);
        return b;
    }

    private Map<String, ButtonGroup> mapButtonGroup = new HashMap<String, ButtonGroup>();

    protected final ButtonGroup createButtonGroup(String name) {
        ButtonGroup b = mapButtonGroup.get(name);
        if (b != null)
            return b;

        b = new ButtonGroup();

        mapButtonGroup.put(name, b);
        return b;
    }

    protected final JProgressBar createProgressBar(final String name) {
        JProgressBar b = (JProgressBar) componentMap.get(name);
        if (b != null)
            return b;

        b = new JProgressBar();
        b.setName(name);
        applyResources(b);

        componentMap.put(name, b);
        return b;
    }

    /**
     * 
     * @param <T>
     * @param type
     * @param name
     * @return
     */

    @SuppressWarnings("unchecked")
    protected final <T extends JPanel> T createPanel(Class<T> type, String name) {
        try {
            T p = (T) componentMap.get(name);
            if (p != null)
                return p;

            p = type.newInstance();
            p.setName(name);
            applyResources(p);

            componentMap.put(name, p);
            return (T) p;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    protected final JMenuBar createMenuBar(String name) {
        JMenuBar menuBar = (JMenuBar) componentMap.get(name);
        if (menuBar != null)
            return menuBar;

        menuBar = new JMenuBar();
        menuBar.setDoubleBuffered(true);
        menuBar.setName(name);
        applyResources(menuBar);

        componentMap.put(name, menuBar);
        return menuBar;
    }

    protected final JMenu createMenu(String name) {
        JMenu menu = (JMenu) componentMap.get(name);
        if (menu != null)
            return menu;

        menu = new JMenu();
        menu.setDoubleBuffered(true);
        menu.setName(name);
        applyResources(menu);

        componentMap.put(name, menu);
        return menu;
    }

    protected final JMenuItem createMenuItem(String name, final String eventCode) {
        JMenuItem menu = (JMenuItem) componentMap.get(name);
        if (menu != null)
            return menu;

        menu = new JMenuItem();
        menu.setName(name);
        applyResources(menu);

        menu.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                NSEventManager.fireUIEvent(NSPanel.this, eventCode);
            }
        });

        componentMap.put(name, menu);
        return menu;
    }

    protected final JToolBar createToolbar(String name) {
        JToolBar tb = (JToolBar) componentMap.get(name);
        if (tb != null)
            return tb;

        tb = new JToolBar();
        tb.setName(name);
        applyResources(tb);
        componentMap.put(name, tb);
        return tb;
    }

    /**
     * 
     * @param name
     * @return
     */

    protected final JLabel createLabel(String name) {
        JLabel l = (JLabel) componentMap.get(name);
        if (l != null)
            return l;

        l = new JLabel();
        l.setName(name);
        applyResources(l);

        componentMap.put(name, l);
        return l;
    }

    protected final NSComboBox createCombo(String name) {
        NSComboBox cb = (NSComboBox) componentMap.get(name);
        if (cb != null)
            return cb;

        cb = new NSComboBox();
        cb.setName(name);
        applyResources(cb);

        componentMap.put(name, cb);
        return cb;
    }

    /**
     * 
     * @param name
     * @param displayExpression
     * @param selectionEventCode
     * @return
     */

    protected final NSComboBox createCombo(//
            final String name,//
            final String displayExpression,//
            final String selectionEventCode) {
        return createCombo(name, displayExpression, selectionEventCode, null, null);
    }

    @SuppressWarnings("unchecked")
    protected final NSComboBox createCombo(//
            final String name,//
            final String displayExpression,//
            final String selectionEventCode,//
            final Object userObject,//
            final String userObjectField) {
        NSComboBox cb = (NSComboBox) componentMap.get(name);
        if (cb != null)
            return cb;

        cb = createCombo(name);
        cb.setSelectionEventCode(selectionEventCode);
        cb.setRenderer(new NSComboBoxRenderer(displayExpression));
        cb.setUserObject(userObject);
        cb.setUserObjectField(userObjectField);

        applyResources(cb);
        // cb.setBorder( new LineBorder(Color.BLACK) );

        componentMap.put(name, cb);

        return cb;
    }

    protected final NSCalendarCombo createCalendarCombo(final String name, final Object userObject, final String userObjectField) {
        NSCalendarCombo cb = (NSCalendarCombo) componentMap.get(name);
        if (cb != null)
            return cb;

        cb = new NSCalendarCombo();
        cb.setDateFormat(new SimpleDateFormat("dd-MM-yy HH:mm:ss"));

        applyResources(cb);

        componentMap.put(name, cb);

        return cb;
    }

    protected final JCheckBox createCheckBox(String name) {
        JCheckBox cb = (JCheckBox) componentMap.get(name);
        if (cb != null)
            return cb;

        cb = new JCheckBox();
        cb.setName(name);
        applyResources(cb);

        componentMap.put(name, cb);
        return cb;
    }

    protected final NSTextFieldRegExp createTextField(String name) {
        NSTextFieldRegExp tf = (NSTextFieldRegExp) componentMap.get(name);
        if (tf != null)
            return tf;

        tf = new NSTextFieldRegExp();
        tf.setName(name);
        applyResources(tf);

        componentMap.put(name, tf);
        return tf;
    }

    protected final NSTextAreaRegExp createTextArea(String name) {
        NSTextAreaRegExp tf = (NSTextAreaRegExp) componentMap.get(name);
        if (tf != null)
            return tf;

        tf = new NSTextAreaRegExp();
        tf.setName(name);
        applyResources(tf);

        componentMap.put(name, tf);
        return tf;
    }

    protected final NSTable createTable(//
            String name,//
            final String rowSelection,//
            final String rowDoubleClick) {
        NSTable t = (NSTable) componentMap.get(name);
        if (t != null)
            return t;

        t = new NSTable();
        t.setName(name);
        applyResources(t);

        if (rowSelection != null) {
            final NSTable t0 = t;

            t0.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

                @Override
                public void valueChanged(ListSelectionEvent e) {
                    if (e.getValueIsAdjusting())
                        return;
                    // LOG.debug( e );
                    NSEventManager.dispatchUIEvent(rowSelection, t0.getSelectedItem());
                }
            });
        }

        if (rowDoubleClick != null) {
            final NSTable t0 = t;

            t0.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getClickCount() < 2)
                        return;

                    NSEventManager.dispatchUIEvent(rowDoubleClick, t0.getSelectedItem());
                }
            });
        }

        componentMap.put(name, t);

        return t;
    }

    protected final JPasswordField createPasswordField(//
            String name,//
            final String eventCode) {
        JPasswordField tf = (JPasswordField) componentMap.get(name);
        if (tf != null)
            return tf;

        tf = new JPasswordField();
        tf.setName(name);
        applyResources(tf);

        if (eventCode != null)
            tf.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    NSEventManager.fireUIEvent(NSPanel.this, eventCode);
                }
            });

        componentMap.put(name, tf);
        return tf;
    }

    public void notifyUIEvent(NSEvent event) {
        NSEventManager.dispatch(event);
    }

    protected void showMessage(String msg) {
        JOptionPane.showMessageDialog(this, msg, "Avviso", JOptionPane.INFORMATION_MESSAGE);
    }

    protected void showWarning(String msg) {
        JOptionPane.showMessageDialog(this, msg, "Avviso", JOptionPane.WARNING_MESSAGE);
    }

    protected int showOptions(Object message, String title, String[] options) {
        int ret = JOptionPane.showOptionDialog(this, message, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
        return ret;
    }

    protected void showErrorMessage(String msg) {
        JOptionPane.showMessageDialog(this, msg, "Errore", JOptionPane.ERROR_MESSAGE);
    }

    protected void showErrorMessage(Exception e) {
        String msg = e.getMessage();
        if (e instanceof NullPointerException)
            msg = "NullPointerException";

        JOptionPane.showMessageDialog(this, msg, "Errore", JOptionPane.ERROR_MESSAGE);
    }

    @Override
    public void finalize() throws Throwable {
        super.finalize();
        NSEventManager.getInstance().removeUIListener(this);
    }

}
